package com.example.service;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolver;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

@Service
public class NettyServiceImpl extends ChannelInitializer implements NettyService, InitializingBean, CommandLineRunner, DisposableBean {

    private EventLoopGroup bossGroup;

    private EventLoopGroup workGroup;

    @Value("${netty.port}")
    private int port;

    private ServerBootstrap serverBootstrap;

    Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void destroy() throws Exception {
        if (!this.bossGroup.isShutdown()) {
            this.bossGroup.shutdownGracefully();
        }
        if (!this.workGroup.isShutdown()) {
            this.workGroup.shutdownGracefully();
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.bossGroup = new NioEventLoopGroup(1);
        this.workGroup = new NioEventLoopGroup(10);
        this.serverBootstrap = new ServerBootstrap().
                group(this.bossGroup, this.workGroup).channel(NioServerSocketChannel.class).
                childHandler(this).
                localAddress(this.port).
                option(ChannelOption.SO_BACKLOG, 1024).
                childOption(ChannelOption.SO_KEEPALIVE, true);
    }

    @Override
    public void run(String... args) throws Exception {
        this.serverBootstrap.bind(this.port).sync();
        logger.warn("netty server started...");
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ClassResolver classResolver = ClassResolvers.cacheDisabled(getClass().getClassLoader());
        ch.pipeline().addLast("decoder", new ObjectDecoder(classResolver));
        ch.pipeline().addLast("encoder", new ObjectEncoder());
        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {

            @Override
            public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
                super.channelRegistered(ctx);
                logger.warn("NettyServiceImpl channelRegistered ...");
            }

            @Override
            public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
                super.channelUnregistered(ctx);
            }

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                super.channelActive(ctx);
                logger.warn("NettyServiceImpl channelActive ...");
            }

            @Override
            public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                super.channelInactive(ctx);
            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                super.channelRead(ctx, msg);
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                super.channelReadComplete(ctx);
            }

            @Override
            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                super.userEventTriggered(ctx, evt);
            }

            @Override
            public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
                super.channelWritabilityChanged(ctx);
            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                super.exceptionCaught(ctx, cause);
            }
        });
    }
}
